package org.maxgroundstation.groundstation.view.overlay;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.text.DecimalFormat;
import java.util.Map;

import org.maxgroundstation.groundstation.GroundStation;
import org.maxgroundstation.groundstation.comm.service.gps.GpsService;

/**
 * An overlay for displaying rover GPS data.
 * @author Scott Davis
 * 
 * Copyright (C) 2010  Scott Davis
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
public class GpsOverlay implements Overlay {

    private static final char DEGREE_SYMBOL = '\u00B0';
    private static final String[] COMPASS_DIRECTIONS = { 
        "N", "NE", "E", "SE", "S", "SW", "W", "NW", "N" };
    
    // Data members.
    private GroundStation gs;
    private boolean display;
    private DecimalFormat formatter;
    private DecimalFormat positionFormatter;
    
    /**
     * Constructor
     * @param gs the ground station.
     */
    public GpsOverlay(GroundStation gs) {
        // Initialize data members.
        this.gs = gs;
        display = true;
        formatter = new DecimalFormat("0.0");
        positionFormatter = new DecimalFormat("0.000000");
    }
    
    @Override
    public boolean canDisplay() {
        return gs.getServiceComm().hasService(GpsService.SERVICE_ID);
    }

    @Override
    public boolean getDisplay() {
        return display;
    }
    
    @Override
    public void setDisplay(boolean displayOverlay) {
        this.display = displayOverlay;
    }

    @Override
    public String getName() {
        return "GPS Overlay";
    }

    /**
     * Gets the rover gps service .
     * @return gps service or null if none.
     */
    private GpsService getGpsService() {
        return (GpsService) gs.getServiceComm().getService(GpsService.SERVICE_ID);
    }
    
    @Override
    public void paintOverlay(Graphics g, int panelWidth, int panelHeight) {
        Graphics2D g2d = (Graphics2D) g;
        
        g2d.setColor(Color.GREEN);
        g2d.setFont(new Font(g2d.getFont().getFontName(), Font.BOLD, 10));
        
        Map<String, String> gpsMap = getGpsService().getGpsMap();
        if (gpsMap != null) {
            
            // Draw latitude string.
            if (gpsMap.containsKey("lat")) {
                float latitude = Float.parseFloat(gpsMap.get("lat"));
                String latitudeString = getFormattedLatitudeString(latitude);
                TextDisplayUtil.drawText(latitudeString, 10, 15, 
                        TextDisplayUtil.LEFT_HORIZONTAL_ALIGNMENT, 
                        TextDisplayUtil.TOP_VERTICAL_ALIGNMENT, g2d);
            }
            
            // Draw longitude string.
            if (gpsMap.containsKey("lon")) {
                float longitude = Float.parseFloat(gpsMap.get("lon"));
                String longitudeString = getFormattedLongitudeString(longitude);
                TextDisplayUtil.drawText(longitudeString, 10, 30, 
                        TextDisplayUtil.LEFT_HORIZONTAL_ALIGNMENT, 
                        TextDisplayUtil.TOP_VERTICAL_ALIGNMENT, g2d);
            }
            
            // Draw heading string.
            if (gpsMap.containsKey("heading")) {
                float heading = Float.parseFloat(gpsMap.get("heading"));
                String headingString = getFormattedHeadingString(heading);
                TextDisplayUtil.drawText(headingString, 10, 45, 
                        TextDisplayUtil.LEFT_HORIZONTAL_ALIGNMENT, 
                        TextDisplayUtil.TOP_VERTICAL_ALIGNMENT, g2d);
            }
            
            // Draw speed string.
            if (gpsMap.containsKey("speed")) {
                float speed = Float.parseFloat(gpsMap.get("speed"));
                String speedString = getFormattedSpeedString(speed);
                TextDisplayUtil.drawText(speedString, 10, 60, 
                        TextDisplayUtil.LEFT_HORIZONTAL_ALIGNMENT, 
                        TextDisplayUtil.TOP_VERTICAL_ALIGNMENT, g2d);
            }
            
            // Draw elevation string.
            if (gpsMap.containsKey("elevation")) {
                float elevation = Float.parseFloat(gpsMap.get("elevation"));
                String elevationString = getFormattedElevationString(elevation);
                TextDisplayUtil.drawText(elevationString, 10, 75, 
                        TextDisplayUtil.LEFT_HORIZONTAL_ALIGNMENT, 
                        TextDisplayUtil.TOP_VERTICAL_ALIGNMENT, g2d);
            }
            
            // Draw satellites string.
            if (gpsMap.containsKey("satellites")) {
                int satellites = Integer.parseInt(gpsMap.get("satellites"));
                String satellitesString = getFormattedSatellitesString(satellites);
                TextDisplayUtil.drawText(satellitesString, 10, 90, 
                        TextDisplayUtil.LEFT_HORIZONTAL_ALIGNMENT, 
                        TextDisplayUtil.TOP_VERTICAL_ALIGNMENT, g2d);
            }
            
            // Draw HDOP string.
            if (gpsMap.containsKey("hdop")) {
                float hdop = Float.parseFloat(gpsMap.get("hdop"));
                String hdopString = getFormattedHdopString(hdop);
                TextDisplayUtil.drawText(hdopString, 10, 105, 
                        TextDisplayUtil.LEFT_HORIZONTAL_ALIGNMENT, 
                        TextDisplayUtil.TOP_VERTICAL_ALIGNMENT, g2d);
            }
        }
    }

    /**
     * Gets the formatted latitude string.
     * @param latitude the latitude from -90 degrees (south) to +90 degrees (north).
     * @return formatted string.
     */
    private String getFormattedLatitudeString(float latitude) {
        String hemisphereString = "";
        if (latitude > 0) hemisphereString = "N";
        else if (latitude < 0) hemisphereString = "S";
        String latitudeString = positionFormatter.format(Math.abs(latitude));
        return "Latitude: " + latitudeString + DEGREE_SYMBOL + " " + hemisphereString;
    }
    
    /**
     * Gets the formatted longitude string.
     * @param longitude the longitude from -180 degrees (west) to +180 degrees (east).
     * @return formatted string.
     */
    private String getFormattedLongitudeString(float longitude) {
        String hemisphereString = "";
        if (longitude > 0) hemisphereString = "E";
        else if (longitude < 0) hemisphereString = "W";
        String longitudeString = positionFormatter.format(Math.abs(longitude));
        return "Longitude: " + longitudeString + DEGREE_SYMBOL + " " + hemisphereString;
    }
    
    /**
     * Gets the formatted heading string.
     * @param heading the heading from 0 degrees to 360 degrees clockwise from north.
     * @return formatted string.
     */
    private String getFormattedHeadingString(float heading) {
        // Check range of heading.
        while (heading < 0) heading += 360;
        while (heading > 360) heading -= 360;
        
        // Get compass direction.
        float compassHeading = (heading / 45F) + .5F;
        if (compassHeading > 8F) compassHeading -= 8F;
        String compassDirection = COMPASS_DIRECTIONS[(int) compassHeading];
        
        String headingString = formatter.format(heading);
        
        return "Movement Heading: " + headingString + DEGREE_SYMBOL + " " + compassDirection;
    }
    
    /**
     * Gets the formatted speed string.
     * @param speed the speed (m/s).
     * @return formatted string.
     */
    private String getFormattedSpeedString(float speed) {
        String speedString = formatter.format(speed);
        return "Speed: " + speedString + " m/s";
    }
    
    /**
     * Gets the formatted elevation string.
     * @param elevation the elevation (meters above sea level).
     * @return formatted string.
     */
    private String getFormattedElevationString(float elevation) {
        String elevationString = formatter.format(elevation);
        return "Elevation: " + elevationString + " m";
    }
    
    /**
     * Gets the formatted satellites string.
     * @param satellites the number of satellites.
     * @return formatted string.
     */
    private String getFormattedSatellitesString(int satellites) {
        return "Number of Satellites: " + satellites;
    }
    
    /**
     * Gets the formatted HDOP string.
     * @param hdop the hdop value.
     * @return formatted string.
     */
    private String getFormattedHdopString(float hdop) {
        String hdopString = formatter.format(hdop);
        return "HDOP: " + hdopString;
    }
}